#include "main_matrix.h"
#include <math.h>
#include <stdio.h>
#include "matrix.h"
#include "opencl_tools.h"
#include "params.h"
#include "point.h"

tData ***currentMatrix = NULL;
tData ***oldMatrix = NULL;

Point3 localMatrixOffset, localMatrixSize;

int GetLocalPosDeb(int lmpi_rank);
int GetLocalPosFin(int lmpi_rank);

void MainMatrixAllocate();
void MainMatrixInitValues();
void MainMatrixInitBlockValues(int x, int y, int z);

Point3 GetRealPos(Point3 blockPos);

int exchangeDiviser = 1;

void MainMatrixInit(){
    MainMatrixFree();
    MainMatrixAllocate();

    MainMatrixInitValues();
    Matrix3Copy(currentMatrix, oldMatrix, localMatrixOffset, localMatrixSize);
}

void MainMatrixInitValues(){
    for(int x = 0; x < nbNodes.x; x++)
        for(int y = 0; y < nbNodes.y; y++)
            for(int z = 0; z < nbNodes.z; z++)
                MainMatrixInitBlockValues(x, y, z);
}

void MainMatrixInitBlockValues(int x, int y, int z){
    int mat = fuelCellMap[x][y][z];
    tData tInit = tMaterial[mat].Tinit;

    Point3 pos = GetRealPos( Point3i(x, y, z) );

    for(int dx = 0; dx < blockNbNodesX->list[x]; dx++){
        if( dx + pos.x < localMatrixOffset.x 
                || dx + pos.x >= localMatrixOffset.x + localMatrixSize.x )
            continue;
        for(int dy = 0; dy < blockNbNodesY->list[y]; dy++)
            for(int dz = 0; dz < blockNbNodesZ->list[z]; dz++){
                currentMatrix[pos.x + dx][pos.y + dy][pos.z + dz] = tInit;
            }
    }
}

void MainMatrixAllocate(){
    localMatrixOffset = ComputeLocalMatrixOffset(0); // mpi_rank instead of 0
    localMatrixSize = ComputeLocalMatrixSize(0); // mpi_rank instead of 0

    message("offset(%d; %d; %d) size(%d; %d; %d)", 
            localMatrixOffset.x, localMatrixOffset.y, localMatrixOffset.z, 
            localMatrixSize.x, localMatrixSize.y, localMatrixSize.z);
        // On node 0, a bigger matrix is allocated, to be able to retrieve result easily.
        currentMatrix = (tData ***)Matrix3Allocate(
                localMatrixOffset, matrixSize, sizeof(tData));
        oldMatrix =     (tData ***)Matrix3Allocate(
                localMatrixOffset, matrixSize, sizeof(tData));
}

void MainMatrixFree(){
    if( currentMatrix != NULL )
        Matrix3Free((void ***)currentMatrix, localMatrixOffset, sizeof(tData));
    if( oldMatrix != NULL )
        Matrix3Free((void ***)oldMatrix, localMatrixOffset, sizeof(tData));
}

int GetLocalPosDeb(int lmpi_rank){
    float rank = (float)lmpi_rank;
    //~ float size_x = (float)matrixSize.x / (float)mpi_size;
    float size_x = (float)matrixSize.x;
printf("\n\npos_deb=%f\n\n",size_x);
    int pos_deb = round( rank * size_x );
}

int GetLocalPosFin(int lmpi_rank){
    float rank = (float)lmpi_rank;
    //~ float size_x = (float)matrixSize.x / (float)mpi_size;
    float size_x = (float)matrixSize.x;
    int pos_fin = round( (rank + 1) * size_x ) - 1;
    return pos_fin;
}


Point3 ComputeLocalMatrixOffset(int lmpi_rank){
    int pos_deb = GetLocalPosDeb(lmpi_rank);

    return Point3i(pos_deb, 0, 0);
}

Point3 ComputeLocalMatrixSize(int lmpi_rank){
    Point3 localMatrixSize;
    int pos_deb = GetLocalPosDeb(lmpi_rank);
    int pos_fin = GetLocalPosFin(lmpi_rank);

    localMatrixSize.x = pos_fin - pos_deb + 1;
    localMatrixSize.y = matrixSize.y;
    localMatrixSize.z = matrixSize.z;

    return localMatrixSize;
}

Point3 GetRealPos(Point3 blockPos){
    Point3 realPos = zero3;

    for(int i = 0; i < blockPos.x; i++)
        realPos.x += blockNbNodesX->list[i];
    for(int i = 0; i < blockPos.y; i++)
        realPos.y += blockNbNodesY->list[i];
    for(int i = 0; i < blockPos.z; i++)
        realPos.z += blockNbNodesZ->list[i];

    return realPos;
}

